package com.zust.zst.aop;

import com.zust.zst.common.vo.ResultVO;
import com.zust.zst.common.vo.login.LoginVO;
import com.zust.zst.token.GlobalTokenManagement;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

/**
 * AOP增强Token鉴权
 *
 * @author gavin
 * @date 2022/11/14 18:13
 */

@Component
@Aspect
@Slf4j
public class HttpAuthCheckerAdviseDefine {

    private static final String TOKEN = "token";

    /**
     * 角色-type值哈希表
     */
    private static Map<String, Integer> roleMap = new HashMap<>(16);

    /**
     * 初始化：角色-type值哈希表
     */
    @PostConstruct
    private void init() {
        roleMap.put("admin", 1);
        roleMap.put("teacher", 2);
        roleMap.put("student", 3);
    }

    @Pointcut("@annotation(com.zust.zst.aop.HttpAuthChecker)")
    public void httpAuthCheckerPointCut() {
    }

    @Pointcut("@annotation(com.zust.zst.aop.HttpRoleChecker)")
    public void httpRoleCheckerPointCut() {
    }

    /**
     * 定义 httpAuthCheckerPointCut的advise
     */
    @Around("httpAuthCheckerPointCut()")
    public Object checkAuth(ProceedingJoinPoint joinPoint) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes()))
                .getRequest();

        // 检查用户所传递的 token 是否合法
        String token = getUserToken(request);
        // token池找不到token，则报错
        if (!GlobalTokenManagement.tokenMap.containsKey(token)) {
            log.warn("找不到token，请重新登录!");
            return ResultVO.error("找不到token，请重新登录");
        }

        return joinPoint.proceed();
    }

    /**
     * 定义 httpRoleCheckerPointCut的advise
     */
    @Around("httpRoleCheckerPointCut() && @annotation(httpRoleChecker)")
    public Object checkRole(ProceedingJoinPoint joinPoint, HttpRoleChecker httpRoleChecker) throws Throwable {
        HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes()))
                .getRequest();

        // 获取访问者的token
        String token = getUserToken(request);

        LoginVO loginVO = GlobalTokenManagement.tokenMap.get(token);
        // 获取可以访问接口的用户类别
        String[] values = httpRoleChecker.value();
        // 获取当前访问者的用户类别
        int loginVal = loginVO.getType();

        boolean flag = false;
        for (String value : values) {
            if (loginVal == roleMap.get(value)) {
                flag = true;
                break;
            }
        }
        if (flag) {
            return joinPoint.proceed();
        }else {
            log.warn("当前用户无权限访问本接口！");
            return ResultVO.error("当前用户无权限访问本接口！");
        }
    }

    /**
     * 获得前端传来的token
     */
    private String getUserToken(HttpServletRequest request) {
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String key = headerNames.nextElement();
            if (key.equals(TOKEN)) {
                if (request.getHeader(key) != null && !Objects.equals(request.getHeader(key), "")) {
                    return request.getHeader(key);
                }
            }
        }
        return "";
    }
}